/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.tools.idea.npw.actions

import com.android.AndroidProjectTypes
import com.android.tools.idea.gradle.dsl.api.ProjectBuildModel
import com.android.tools.idea.model.AndroidModel
import com.android.tools.idea.model.StudioAndroidModuleInfo
import com.android.tools.idea.npw.COMPOSE_MIN_AGP_VERSION
import com.android.tools.idea.npw.hasComposeMinAgpVersion
import com.android.tools.idea.npw.model.ProjectSyncInvoker.DefaultProjectSyncInvoker
import com.android.tools.idea.npw.model.RenderTemplateModel.Companion.fromFacet
import com.android.tools.idea.npw.project.getModuleTemplates
import com.android.tools.idea.npw.project.getPackageForPath
import com.android.tools.idea.npw.template.ConfigureTemplateParametersStep
import com.android.tools.idea.npw.template.TemplateResolver
import com.android.tools.idea.projectsystem.getModuleSystem
import com.android.tools.idea.wizard.model.ModelWizard
import com.android.tools.idea.wizard.template.Category
import com.android.tools.idea.wizard.template.TemplateConstraint
import com.android.tools.idea.wizard.template.WizardUiContext
import com.android.tools.idea.wizard.ui.SimpleStudioWizardLayout
import com.android.tools.idea.wizard.ui.StudioWizardDialogBuilder
import com.google.wireless.android.sdk.stats.AndroidStudioEvent.TemplatesUsage.TemplateComponent.WizardUiContext.MENU_GALLERY
import com.intellij.icons.AllIcons
import com.intellij.openapi.actionSystem.ActionUpdateThread
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.actionSystem.DataKey
import com.intellij.openapi.actionSystem.PlatformCoreDataKeys
import com.intellij.openapi.project.Project
import icons.StudioIcons
import java.io.File
import org.jetbrains.android.facet.AndroidFacet
import org.jetbrains.android.refactoring.isAndroidx
import org.jetbrains.android.util.AndroidBundle

// These categories will be using a new wizard
val NEW_WIZARD_CATEGORIES =
  setOf(Category.Activity, Category.Google, Category.Car, Category.Compose)
@JvmField val CREATED_FILES = DataKey.create<MutableList<File>>("CreatedFiles")

private fun defaultShowWizardDialog(
  modelWizard: ModelWizard,
  dialogTitle: String,
  project: Project,
): Unit =
  StudioWizardDialogBuilder(modelWizard, dialogTitle)
    .setProject(project)
    .build(SimpleStudioWizardLayout())
    .show()

/** An action to launch a wizard to create a component from a template. */
// TODO(qumeric): consider accepting [Template] instead?
data class NewAndroidComponentAction
@JvmOverloads
constructor(
  private val category: Category,
  private val templateName: String,
  private val minSdkApi: Int,
  private val templateConstraints: Collection<TemplateConstraint> = setOf(),
  private val showWizardDialog: (ModelWizard, String, Project) -> Unit = ::defaultShowWizardDialog,
) :
  AnAction(
    templateName,
    AndroidBundle.message("android.wizard.action.new.component", templateName),
    null,
  ) {

  @Deprecated("Please use the main constructor")
  constructor(
    category: String,
    templateName: String,
    minSdkApi: Int,
  ) : this(Category.values().find { it.name == category }!!, templateName, minSdkApi)

  var shouldOpenFiles = true

  private val isActivityTemplate: Boolean
    get() = NEW_WIZARD_CATEGORIES.contains(category)

  private val isJourneysTemplate: Boolean
    get() = templateName == "Journey File"

  init {
    templatePresentation.icon =
      when {
        isActivityTemplate -> StudioIcons.Shell.Filetree.ACTIVITY
        isJourneysTemplate -> AllIcons.FileTypes.Xml
        else -> StudioIcons.Shell.Filetree.ANDROID_FILE
      }
  }

  override fun getActionUpdateThread() = ActionUpdateThread.BGT

  @Suppress("DialogTitleCapitalization")
  override fun update(e: AnActionEvent) {
    val module = PlatformCoreDataKeys.MODULE.getData(e.dataContext) ?: return
    val moduleInfo = StudioAndroidModuleInfo.getInstance(module) ?: return
    val presentation = e.presentation
    presentation.isVisible = true
    // See also com.android.tools.idea.npw.template.ChooseActivityTypeStep#validateTemplate
    when {
      minSdkApi > moduleInfo.minSdkVersion.featureLevel -> {
        presentation.text =
          AndroidBundle.message("android.wizard.action.requires.minsdk", templateName, minSdkApi)
        presentation.isEnabled = false
      }
      templateConstraints.contains(TemplateConstraint.AndroidX) && !module.project.isAndroidx() -> {
        presentation.text =
          AndroidBundle.message("android.wizard.action.requires.androidx", templateName)
        presentation.isEnabled = false
      }
      templateConstraints.contains(TemplateConstraint.Compose) &&
        !hasComposeMinAgpVersion(module.project) -> {
        presentation.text =
          AndroidBundle.message(
            "android.wizard.action.requires.new.agp",
            templateName,
            COMPOSE_MIN_AGP_VERSION,
          )
        presentation.isEnabled = false
      }
      templateConstraints.contains(TemplateConstraint.Aidl) &&
        ProjectBuildModel.get(module.project)
          .getModuleBuildModel(module)
          ?.android()
          ?.buildFeatures()
          ?.aidl()
          ?.toBoolean() != true -> {
        presentation.text =
          AndroidBundle.message("android.wizard.action.requires.aidlEnabled", templateName)
        presentation.isEnabled = false
      }
      else -> {
        val facet = AndroidFacet.getInstance(module)
        val isProjectReady =
          facet != null &&
            AndroidModel.get(facet) != null &&
            facet.configuration.projectType != AndroidProjectTypes.PROJECT_TYPE_INSTANTAPP
        presentation.isEnabled = isProjectReady
      }
    }
  }

  override fun actionPerformed(e: AnActionEvent) {
    val module = PlatformCoreDataKeys.MODULE.getData(e.dataContext) ?: return
    val facet = AndroidFacet.getInstance(module) ?: return
    if (AndroidModel.get(facet) == null) {
      return
    }
    var targetDirectory = CommonDataKeys.VIRTUAL_FILE.getData(e.dataContext)
    // If the user selected a simulated folder entry (eg "Manifests"), there will be no target
    // directory
    if (targetDirectory != null && !targetDirectory.isDirectory) {
      targetDirectory = targetDirectory.parent
      assert(targetDirectory != null)
    }
    val activityDescription = e.presentation.text // e.g. "Empty Activity", "Tabbed Activity"
    // TODO(qumeric): always show all available templates but preselect a good default
    val moduleTemplates =
      facet.getModuleTemplates(targetDirectory).filter {
        // Do not allow to create Android Components from the templates into source sets without a
        // source root.
        // This will filter out androidTest and unit tests source sets.
        it.paths.getSrcDirectory(null) != null
      }
    assert(moduleTemplates.isNotEmpty())
    val initialPackageSuggestion =
      if (targetDirectory == null) facet.getModuleSystem().getPackageName()
      else facet.getPackageForPath(moduleTemplates, targetDirectory)
    val templateModel =
      fromFacet(
        facet,
        initialPackageSuggestion,
        moduleTemplates[0],
        "New $activityDescription",
        DefaultProjectSyncInvoker(),
        shouldOpenFiles,
        MENU_GALLERY,
      )
    val newActivity =
      TemplateResolver.getAllTemplates()
        .filter { WizardUiContext.MenuEntry in it.uiContexts }
        .find { it.name == templateName }

    templateModel.newTemplate = newActivity!!

    val dialogTitle =
      AndroidBundle.message(
        when {
          isActivityTemplate -> "android.wizard.new.activity.title"
          isJourneysTemplate -> "android.wizard.new.journey.title"
          else -> "android.wizard.new.component.title"
        }
      )
    val stepTitle =
      AndroidBundle.message(
        when {
          isActivityTemplate -> "android.wizard.config.activity.title"
          isJourneysTemplate -> "android.wizard.config.journey.title"
          else -> "android.wizard.config.component.title"
        }
      )
    val wizardBuilder =
      ModelWizard.Builder().apply {
        // TODO: Allow the template to configure whether to show the target source set picker
        // Journey tests are not compiled against any particular build variant
        // and can be run against any variant, so showing this option doesn't make sense
        val showTargetSourceSetPicker = !isJourneysTemplate
        addStep(
          ConfigureTemplateParametersStep(
            templateModel,
            stepTitle,
            moduleTemplates,
            showTargetSourceSetPicker,
          )
        )
      }

    showWizardDialog(wizardBuilder.build(), dialogTitle, module.project)
    e.dataContext.getData(CREATED_FILES)?.addAll(templateModel.createdFiles)
  }
}
